﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.ComponentModel;
using System.Data;
using System.Windows.Documents;
using System.Windows.Media;
using System.Globalization;
using Registration.Entities;


namespace Registration
{

    static public class CollectionHelper
    {

        public static DataTable ConvertTo<T>(IList<T> list)
        {
            var table = CreateTable<T>();
            try
            {
                var entityType = typeof(T);
                var properties = TypeDescriptor.GetProperties(entityType);

                foreach (var item in list)
                {
                    var row = table.NewRow();

                    foreach (PropertyDescriptor prop in properties)
                    {
                        row[prop.Name] = prop.GetValue(item);
                    }

                    table.Rows.Add(row);
                }
            }
            finally
            {
                table.Dispose();
            }

            return table;
        }

        public static IList<T> ConvertTo<T>(IList<DataRow> rows)
        {
            IList<T> list = null;

            if (rows != null)
            {
                list = rows.Select(row => CreateItem<T>(row)).ToList();
            }

            return list;
        }

        public static IList<T> ConvertTo<T>(DataTable table)
        {
            if (table == null) return null;
            var rows = table.Rows.Cast<DataRow>().ToList();
            return ConvertTo<T>(rows);
        }

        public static T CreateItem<T>(DataRow row)
        {
            var obj = default(T);
            if (row != null)
            {
                obj = Activator.CreateInstance<T>();

                foreach (DataColumn column in row.Table.Columns)
                {
                    var prop = obj.GetType().GetProperty(column.ColumnName);
                    var value = row[column.ColumnName];
                    prop.SetValue(obj, value, null);
                }
            }

            return obj;
        }

        public static DataTable CreateTable<T>()
        {
            var entityType = typeof(T);
            var table = new DataTable(entityType.Name);
            try
            {
                var properties = TypeDescriptor.GetProperties(entityType);

                foreach (PropertyDescriptor prop in properties)
                {
                    table.Columns.Add(prop.Name, prop.PropertyType);
                }
            }
            finally
            {
                table.Dispose();
            }

            return table;
        }
    }
    
    public static class GlobalData
    {
        public static string FontName {get{ return "Cabri"; }}
        public static int FontSize {get { return 12; }}
    }
    
    public class BaseDataSetPaginator : DocumentPaginator
    {
        protected DataTable Dt;

        public string RaceNames { get; set; }

        // Could be wrapped with public properties that call PaginateData() when set.
        protected Typeface MyTypeface;
        protected double FontSize;
        protected double Margin;

        public BaseDataSetPaginator(DataTable dt, Typeface myTypeface, double fontSize, double margin, Size pageSize)
        {
            Dt = dt;
            MyTypeface = myTypeface;
            FontSize = fontSize;
            Margin = margin;
            _pageSize = pageSize;
            PaginateData();
        }

        public override bool IsPageCountValid
        {
            get { return true; }
        }

        private int _pageCount;
        public override int PageCount
        {
            get { return _pageCount; }
        }

        private Size _pageSize;
        public override Size PageSize
        {
            get
            {
                return _pageSize;
            }
            set
            {
                _pageSize = value;
                PaginateData();
            }
        }

        public override IDocumentPaginatorSource Source
        {
            get { return null; }
        }

        public virtual DocumentPage DoGetPage(int pageNumber)
        {
            return null;
        }

        public override DocumentPage GetPage(int pageNumber)
        {
            return DoGetPage(pageNumber);
        }

        // This helper method splits the data into pages.
        // In some cases you'll need to store objects representing the per-page data.
        // Here, a rowsPerPage value is enough becuase every page is the same.
        protected int RowsPerPage;
        protected void PaginateData()
        {
            // Create a test string for the purposes of measurement.
            FormattedText text = GetFormattedText("A");

            // Count the lines that fit on a page.
            RowsPerPage = (int)((_pageSize.Height - Margin * 2) / text.Height);

            // Leave a row for the headings
            RowsPerPage -= 1;

            _pageCount = (int)Math.Ceiling((double)Dt.Rows.Count / RowsPerPage);
        }

        protected FormattedText GetFormattedText(string text)
        {
            return GetFormattedText(!String.IsNullOrEmpty(text) ? text : "   ", MyTypeface);
        }

        protected FormattedText GetFormattedText(string text, Typeface typeface)
        {
            return !String.IsNullOrEmpty(text)
                       ? new FormattedText(
                             text, CultureInfo.CurrentCulture, FlowDirection.LeftToRight,
                             typeface, FontSize, Brushes.Black)
                       : new FormattedText(
                             "    ", CultureInfo.CurrentCulture, FlowDirection.LeftToRight,
                             typeface, FontSize, Brushes.Black);
        }
    }

    public class ResultDataSetPaginator : BaseDataSetPaginator
    {

        public ResultDataSetPaginator(IEnumerable<Result> results, DataTable dt, Typeface myTypeface, double fontSize, double margin, Size pageSize)
            : base(dt, myTypeface, fontSize, margin, pageSize)
        {
            if (results == null) throw new ArgumentNullException("results");
            var cats = new StringBuilder();
            cats.AppendLine(string.Join(", ", (from r in results select r.CategoryName).Distinct().ToArray()));
            RaceNames = cats.ToString();
        }

        public override DocumentPage DoGetPage(int pageNumber)
        {
            // Create a test string for the purposes of measurement.
            FormattedText text = GetFormattedText("A");
            // Size columns relative to the width of one "A" letter.
            // It's a shortcut that works in this example.
            double col1X = Margin;
            double col2X = col1X + text.Width * 7;
            double col3X = col2X + text.Width * 7;
            double col4X = col3X + text.Width * 7;
            double col5X = col4X + text.Width * 15;
            double col6X = col5X + text.Width * 15;
            double col7X = col6X + text.Width * 15;


            // Calculate the range of rows that fits on this page.
            int minRow = pageNumber * RowsPerPage;
            int maxRow = minRow + RowsPerPage;

            // Create the visual for the page.
            var visual = new DrawingVisual();

            // Initially, set the position to the top-left corner of the printable area.
            var point = new Point(Margin, Margin);

            // Print the column values.
            DrawingContext dc;
            using (dc = visual.RenderOpen())
            {
                // Draw the column headers.
                var columnHeaderTypeface = new Typeface(MyTypeface.FontFamily, FontStyles.Normal, FontWeights.Bold, FontStretches.Normal);

                point.X = col1X;
                text = GetFormattedText("Results for: " + RaceNames, columnHeaderTypeface);
                dc.DrawText(text, point);
                point.Y += text.Height;

                point.X = col1X;
                text = GetFormattedText("Overall", columnHeaderTypeface);
                dc.DrawText(text, point);

                text = GetFormattedText("Place", columnHeaderTypeface);
                point.X = col2X;
                dc.DrawText(text, point);

                text = GetFormattedText("Bib", columnHeaderTypeface);
                point.X = col3X;
                dc.DrawText(text, point);

                text = GetFormattedText("Category", columnHeaderTypeface);
                point.X = col4X;
                dc.DrawText(text, point);

                text = GetFormattedText("First Name", columnHeaderTypeface);
                point.X = col5X;
                dc.DrawText(text, point);

                text = GetFormattedText("Last Name", columnHeaderTypeface);
                point.X = col6X;
                dc.DrawText(text, point);

                text = GetFormattedText("Club", columnHeaderTypeface);
                point.X = col7X;
                dc.DrawText(text, point);

                // Draw the line underneath.
                dc.DrawLine(new Pen(Brushes.Black, 2),
                    new Point(Margin, Margin + text.Height),
                    new Point(PageSize.Width - Margin, Margin + text.Height));

                point.Y += text.Height;

                // Draw the column values.
                for (int i = minRow; i < maxRow; i++)
                {
                    // Check for the end of the last (half-filled) page.
                    if (i > (Dt.Rows.Count - 1)) break;

                    point.X = col1X;
                    text = GetFormattedText(Dt.Rows[i]["Place"].ToString());
                    dc.DrawText(text, point);

                    text = GetFormattedText(Dt.Rows[i]["CategoryPlace"].ToString());
                    point.X = col2X;
                    dc.DrawText(text, point);

                    text = GetFormattedText(Dt.Rows[i]["BibNumber"].ToString());
                    point.X = col3X;
                    dc.DrawText(text, point);

                    text = GetFormattedText(Dt.Rows[i]["CategoryName"].ToString());
                    point.X = col4X;
                    dc.DrawText(text, point);

                    text = GetFormattedText(Dt.Rows[i]["FirstName"].ToString());
                    point.X = col5X;
                    dc.DrawText(text, point);

                    text = GetFormattedText(Dt.Rows[i]["LastName"].ToString());
                    point.X = col6X;
                    dc.DrawText(text, point);

                    text = GetFormattedText(Dt.Rows[i]["Club"].ToString());
                    point.X = col7X;
                    dc.DrawText(text, point);


                    point.Y += text.Height;
                }
            }
            return new DocumentPage(visual, PageSize, new Rect(PageSize), new Rect(PageSize));
        }

    }

    public class StartListDataSetPaginator : BaseDataSetPaginator
    {
        public int RiderCount { get; set; }

        public StartListDataSetPaginator(IList<Entry> entries, DataTable dt, Typeface myTypeface, double fontSize, double margin, Size pageSize)
            : base(dt, myTypeface, fontSize, margin, pageSize)
        {
            RiderCount = entries.Count;
            var cats = new StringBuilder();
            cats.AppendLine(string.Join(", ", (from entry in entries select entry.CategoryName).Distinct().ToArray()));
            RaceNames = cats.ToString();            
        }

        public override DocumentPage DoGetPage(int pageNumber)
        {
            // Create a test string for the purposes of measurement.
            FormattedText text = GetFormattedText("A");
            // Size columns relative to the width of one "A" letter.
            // It's a shortcut that works in this example.
            double col1X = Margin;
            double col2X = col1X + text.Width * 15;
            double col3X = col2X + text.Width * 15;
            double col4X = col3X + text.Width * 15;
            double col5X = col4X + text.Width * 15;


            // Calculate the range of rows that fits on this page.
            int minRow = pageNumber * RowsPerPage;
            int maxRow = minRow + RowsPerPage;

            // Create the visual for the page.
            var visual = new DrawingVisual();

            // Initially, set the position to the top-left corner of the printable area.
            var point = new Point(Margin, Margin);

            // Print the column values.
            using (var dc = visual.RenderOpen())
            {
                // Draw the column headers.
                var columnHeaderTypeface = new Typeface(MyTypeface.FontFamily, FontStyles.Normal, FontWeights.Bold, FontStretches.Normal);

                var headertext = String.Format("Startlist for: {0}               Rider Count = {1}", RaceNames.Replace(Environment.NewLine, string.Empty ), RiderCount.ToString());

                point.X = col1X;
                text = GetFormattedText(headertext, columnHeaderTypeface);
                dc.DrawText(text, point);
                point.Y += text.Height;

                point.X = col1X;
                text = GetFormattedText("Bib Number", columnHeaderTypeface);
                dc.DrawText(text, point);
                text = GetFormattedText("License", columnHeaderTypeface);
                point.X = col2X;
                dc.DrawText(text, point);
                text = GetFormattedText("First Name", columnHeaderTypeface);
                point.X = col3X;
                dc.DrawText(text, point);
                text = GetFormattedText("Last Name", columnHeaderTypeface);
                point.X = col4X;
                dc.DrawText(text, point);
                text = GetFormattedText("Club", columnHeaderTypeface);
                point.X = col5X;
                dc.DrawText(text, point);


                // Draw the line underneath.
                dc.DrawLine(new Pen(Brushes.Black, 2),
                    new Point(Margin, Margin + text.Height),
                    new Point(PageSize.Width - Margin, Margin + text.Height));

                point.Y += text.Height;


                // Draw the column values.
                for (int i = minRow; i < maxRow; i++)
                {
                    // Check for the end of the last (half-filled) page.
                    if (i > (Dt.Rows.Count - 1)) break;

                    point.X = col1X;
                    text = GetFormattedText(Dt.Rows[i]["Bib"].ToString());
                    dc.DrawText(text, point);


                    text = GetFormattedText(Dt.Rows[i]["License"].ToString());
                    point.X = col2X;
                    dc.DrawText(text, point);


                    text = GetFormattedText(Dt.Rows[i]["FirstName"].ToString());
                    point.X = col3X;
                    dc.DrawText(text, point);


                    text = GetFormattedText(Dt.Rows[i]["LastName"].ToString());
                    point.X = col4X;
                    dc.DrawText(text, point);


                    text = GetFormattedText(Dt.Rows[i]["Club"].ToString());

                    if (text.Text.Length == 0) text = GetFormattedText("  ");
                    point.X = col5X;
                    dc.DrawText(text, point);

                    point.Y += text.Height;
                }
            }
            return new DocumentPage(visual, PageSize, new Rect(PageSize), new Rect(PageSize));
        }
    }

}

